/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package coalescent.cef.pui.pob;

import cef.pui.pob.*;

import coalescent.br.AMCElement;
import coalescent.br.BRSPopGen;
import coalescent.br.BRWComp;
import coalescent.br.BRs;
import coalescent.data.K69Data.StringK69Data;
import coalescent.data.KC64Data;
import coalescent.data.KC64Data.StringKC64Data;
import coalescent.data.MutableK69Data;
import coalescent.model.K69;
import coalescent.model.KC64;
import coalescent.phylogeny.CheckPPhylogenyAlgo;
import coalescent.phylogeny.GeneTree;

import commons.provider.Providers.FileBasedProvider;

import org.openide.util.NbBundle;

import java.util.List;
import java.util.Properties;

/**
 * Builds various value objects in coalescent-core using string based key-value pairs. The key-value
 * pairs are provided via a {@link Properties} object.
 *
 * @author Susanta Tewari
 * @version 2.0.0
 * @history Created on 12/8/2010
 * @since 2.0.0
 */
@SuppressWarnings("JavaDoc")
public abstract class Builders {

    /**
     * Private constructor to prevent instantiation.
     */
    private Builders() {}

    /**
     * Builder for value type {@code BRSPopGen<?, ?, ?, ?, ?, ?, ?>}. The available properties are:
     * <ul>
     * <li>{@code coalescent.builder.BRSPopGen.delegate = coalescent.cef.pui.pob.Builders$BUILDER_BR_GT | coalescent.cef.pui.pob.Builders$BUILDER_BR_StringKC64Data}</li>
     * </ul>
     *
     * @param props properties object containing builder properties
     * @return a new builder instance for interface {@code CompositeModel}
     */
    public static Builder<BRSPopGen<?, ?, ?, ?, ?, ?, ?>> getBRSPopGenBuilder(Properties props) {

        return new Builder<BRSPopGen<?, ?, ?, ?, ?, ?, ?>>("coalescent.builder.BRSPopGen", props) {

            @Override
            protected BRSPopGen<?, ?, ?, ?, ?, ?, ?> buildFromProps() throws BuilderException {

                throw new BuilderException(NbBundle.getMessage(Builders.class,
                        "Interface_Builder_Msg"));
            }
        };
    }

    /**
     * Builder for interface {@code CheckPPhylogenyAlgo}. The available properties are:
     * <ul>
     * <li>{@code coalescent.builder.CheckPPhylogenyAlgo.algorithm}("GUSFIELD", "FOURGAMETES")</li>
     * </ul>
     *
     * @param props properties object containing builder properties
     * @return a new builder instance for {@code CheckPPhylogenyAlgo}
     */
    public static Builder<CheckPPhylogenyAlgo> getPhylogenyAlgoBuilder(final Properties props) {

        final String prefix = "coalescent.builder.CheckPPhylogenyAlgo";

        return new StringBasedBuilder<CheckPPhylogenyAlgo>(prefix, props, "algorithm") {

            @Override
            protected CheckPPhylogenyAlgo newInstance(String propValue) {
                return CheckPPhylogenyAlgo.valueOf(propValue);
            }
        };
    }

    /**
     * Builder for value type {@code StringK69Data}. The available properties are:
     * <ul>
     * <li>{@code coalescent.builder.StringK69Data.url = stringk69data:xml//URL}<a href="coalescent/cef/pui/pob/doc-files/k69data.xml.txt">file format</a>, must accompany the <a href="coalescent/cef/pui/pob/doc-files/k69data.xsd.txt">schema</a></li>
     * </ul>
     *
     * @param props properties object containing builder properties
     * @return a new builder instance for {@code StringK69Data}
     */
    public static Builder<StringK69Data> getStringK69DataBuilderFileBased(Properties props) {

        return new URLBasedBuilder<StringK69Data>("coalescent.builder.StringK69Data", props) {

            @Override
            protected Class<FileBasedProvider> getDefaultProviderClass() {
                return FileBasedProvider.class;
            }
        };
    }

    /**
     * Builder for value type {@code StringKC64Data}. The available properties are:
     * <ul>
     * <li>{@code coalescent.builder.StringKC64Data.theta = valid double string as specified in } {@link Double#parseDouble(java.lang.String)}</li>
     * <li>{@code coalescent.builder.StringKC64Data.cnForm = valid values are of the format f1^t1_f2^t2... where fi and ti are positive and represent the frequency domain, and the alleles on that frequency domain, respectively}</li>
     * </ul>
     *
     * @param props properties object containing builder properties
     * @return a new builder instance for {@code StringKC64Data}
     */
    public static Builder<StringKC64Data> getStringKC64DataBuilder(final Properties props) {

        final String prefix = "coalescent.builder.StringKC64Data";

        return new StringBasedBuilder<StringKC64Data>(prefix, props, "cnForm") {

            @Override
            protected StringKC64Data newInstance(String propValue) {

                Builder<KC64> kc64Builder = getKC64Builder(props);

                kc64Builder.setPropertyKeyPrefix(prefix);

                return KC64Data.ofCannonicalForm(kc64Builder.build(), propValue);
            }
        };
    }

    /**
     * Builder for value type {@code KC64}. The available properties are:
     * <ul>
     * <li>{@code coalescent.builder.KC64.theta = valid double string as specified in } {@link Double#parseDouble(java.lang.String)}</li>
     * </ul>
     *
     * @param props properties object containing builder properties
     * @return a new builder instance for {@code KC64}
     */
    public static Builder<KC64> getKC64Builder(Properties props) {

        final String prefix = "coalescent.builder.KC64";

        return new StringBasedBuilder<KC64>(prefix, props, "theta") {

            @Override
            protected KC64 newInstance(String propValue) {
                return new KC64(new Double(propValue));
            }
        };
    }

    /**
     * Builder for value type {@code GeneTree}. The available properties are:
     * <ul>
     * <li>{@code coalescent.builder.GeneTree.theta = valid double string as specified in } {@link Double#parseDouble(java.lang.String)}</li>
     * <li>{@code coalescent.builder.GeneTree.url = stringk69data:xml//URL}<a href="coalescent/cef/pui/pob/doc-files/k69data.xml.txt">file format</a>, must accompany the <a href="coalescent/cef/pui/pob/doc-files/k69data.xsd.txt">schema</a></li>
     * </ul>
     *
     * @param props properties object containing builder properties
     * @return a new builder instance for {@code GeneTree}
     */
    public static Builder<GeneTree> getGeneTreeBuilderFileBased(final Properties props) {

        final String prefix = "coalescent.builder.GeneTree";

        return new Builder<GeneTree>(prefix, props) {

            @Override
            protected GeneTree buildFromProps() throws BuilderException {

                Builder<StringK69Data> stringK69DataBuilder =
                    getStringK69DataBuilderFileBased(props);

                stringK69DataBuilder.setPropertyKeyPrefix(prefix);


                // modify theta
                Builder<Double> thetaBuilder = cef.pui.pob.Builders.getDoubleBuilder(prefix, props,
                                                   "theta");
                double theta = thetaBuilder.build();
                StringK69Data originalData = stringK69DataBuilder.build();
                MutableK69Data<String, Integer, StringK69Data> mutableData =
                    originalData.getMutable();

                mutableData.setModel(new K69(theta));

                return GeneTree.getInstance(mutableData.getImmutable());
            }
        };
    }

    /**
     * Builder for value type {@code AMCElement<KC64.EventType, ?>}. The available properties are:
     * <ul>
     * <li>{@code coalescent.builder.GTRecursion.theta = valid double string as specified in } {@link Double#parseDouble(java.lang.String)}</li>
     * <li>{@code coalescent.builder.GTRecursion.url = stringk69data:xml//URL}<a href="coalescent/cef/pui/pob/doc-files/k69data.xml.txt">file format</a>, must accompany the<a href="coalescent/cef/pui/pob/doc-files/k69data.xsd.txt">schema</a></li>
     * </ul>
     *
     * @param props properties object containing builder properties
     * @return a new builder instance for {@code AMCElement<KC64.EventType, ?>}
     */
    public static Builder<AMCElement<KC64.EventType,
                                     ?>> getFBasedGTRecursionBuilder(final Properties props) {

        final String prefix = "coalescent.builder.GTRecursion";

        return new Builder<AMCElement<KC64.EventType, ?>>(prefix, props) {

            @Override
            protected AMCElement<KC64.EventType, ?> buildFromProps() throws BuilderException {

                Builder<GeneTree> fbasedGTBuilder = getGeneTreeBuilderFileBased(props);

                fbasedGTBuilder.setPropertyKeyPrefix(prefix);

                return BRs.getGeneTreeRecursion(fbasedGTBuilder.build());
            }
        };
    }

    /**
     * Builder for value type {@code AMCElement<KC64.EventType, ?>}. The available properties are:
     * <ul>
     * <li>{@code coalescent.builder.StringKC64DataRecursion.theta = valid double string as specified in } {@link Double#parseDouble(java.lang.String)}</li>
     * <li>{@code coalescent.builder.StringKC64DataRecursion.cnForm = valid values are of the format f1^t1_f2^t2... where
     * fi and ti are positive and represent the frequency domain, and the alleles on that frequency
     * domain, respectively}</li>
     * </ul>
     *
     * @param props properties object containing builder properties
     * @return a new builder instance for {@code AMCElement<KC64.EventType, ?>}
     */
    public static Builder<AMCElement<KC64.EventType,
                                     ?>> getStringKC64DataRecursionBuilder(final Properties props) {

        final String prefix = "coalescent.builder.StringKC64DataRecursion";

        return new Builder<AMCElement<KC64.EventType, ?>>(prefix, props) {

            @Override
            protected AMCElement<KC64.EventType, ?> buildFromProps() throws BuilderException {

                Builder<StringKC64Data> fbasedGTBuilder = getStringKC64DataBuilder(props);

                fbasedGTBuilder.setPropertyKeyPrefix(prefix);

                return BRs.getKC64DataRecursion(fbasedGTBuilder.build());
            }
        };
    }

    /**
     * Builder for value type {@code List<ExactRecursionListener>}. The available properties are:
     * <ul>
     * <li>{@code coalescent.builder.BRComputations.exactProb = true | false}</li>
     * <li>{@code coalescent.builder.BRComputations.configCounter = true | false}</li>
     * <li>{@code coalescent.builder.BRComputations.genealogyCounter = true | false}</li>
     * <li>{@code coalescent.builder.BRComputations.configBuilder = true | false}</li>
     * <li>{@code coalescent.builder.BRComputations.genealogyBuilder = true | false}</li>
     * </ul>
     *
     * @param props properties object containing builder properties
     * @return a new builder instance for {@code List<ExactRecursionListener>}
     */
    public static Builder<List<BRWComp<?, ?>>> getBRComputationsBuilder(final Properties props) {
        return new BRComputationsBuilder("coalescent.builder.BRComputations", props);
    }

    /**
     * Builder with same properties as {@link #getFBasedGTRecursionBuilder(java.util.Properties)}.
     */
    public static final class BUILDER_BR_GT extends BuilderDelegate<AMCElement<KC64.EventType, ?>> {

        /**
         * Constructs ...
         *
         *
         * @param props
         */
        public BUILDER_BR_GT(Properties props) {
            super(getFBasedGTRecursionBuilder(props));
        }
    }

    /**
     * Builder with same properties as {@link #getStringKC64DataRecursionBuilder(java.util.Properties)}.
     */
    public static final class BUILDER_BR_StringKC64Data
            extends BuilderDelegate<AMCElement<KC64.EventType, ?>> {

        /**
         * Constructs ...
         *
         *
         * @param props
         */
        public BUILDER_BR_StringKC64Data(Properties props) {
            super(getStringKC64DataRecursionBuilder(props));
        }
    }
}
